package com.fly.hello.web.async;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.servlet.ModelAndView;

import io.swagger.annotations.Api;
import lombok.Data;

/**
 * Callback和DeferredResult用于设置单个结果<BR>
 * 如果有多个结果需要set返回给客户端时，可以使用SseEmitter以及ResponseBodyEmitter<BR>
 * StreamingResponseBody可用来直接写入到response的OutputStream<BR>
 * see: https://blog.csdn.net/f641385712/article/details/88710676
 */
@Controller
@Api(tags = "deferredResult异步请求")
@RequestMapping("/async")
public class DeferredResultController
{
    private final Queue<DeferredResult<String>> responseBodyQueue = new ConcurrentLinkedQueue<>();
    
    private final Queue<DeferredResult<ModelAndView>> mavQueue = new ConcurrentLinkedQueue<>();
    
    private final Queue<DeferredResult<String>> exceptionQueue = new ConcurrentLinkedQueue<>();
    
    @ResponseBody
    @GetMapping("/deferred-result/response-body")
    public DeferredResult<String> deferredResult()
    {
        DeferredResult<String> result = new DeferredResult<>();
        this.responseBodyQueue.add(result);
        return result;
    }
    
    @GetMapping("/deferred-result/model-and-view")
    public DeferredResult<ModelAndView> deferredResultWithView()
    {
        DeferredResult<ModelAndView> result = new DeferredResult<>();
        this.mavQueue.add(result);
        return result;
    }
    
    @ResponseBody
    @GetMapping("/deferred-result/exception")
    public DeferredResult<String> deferredResultWithException()
    {
        DeferredResult<String> result = new DeferredResult<>();
        this.exceptionQueue.add(result);
        return result;
    }
    
    @ResponseBody
    @GetMapping("/deferred-result/timeout-value")
    public DeferredResult<String> deferredResultWithTimeoutValue()
    {
        return new DeferredResult<>(1000L, "Deferred result after timeout");
    }
    
    /**
     * DeferredResult可以通过任何线程来计算返回一个结果
     */
    @Scheduled(fixedRate = 2000)
    public void processQueues()
    {
        for (DeferredResult<String> result : this.responseBodyQueue)
        {
            result.setResult("Deferred result");
            this.responseBodyQueue.remove(result);
        }
        for (DeferredResult<String> result : this.exceptionQueue)
        {
            result.setErrorResult(new IllegalStateException("DeferredResult error"));
            this.exceptionQueue.remove(result);
        }
        for (DeferredResult<ModelAndView> result : this.mavQueue)
        {
            result.setResult(new ModelAndView("views/html", "javaBean", new JavaBean("bar", "apple")));
            this.mavQueue.remove(result);
        }
    }
    
    @ResponseBody
    @ExceptionHandler
    public String handleException(IllegalStateException ex)
    {
        return "Handled exception: " + ex.getMessage();
    }
}

@Data
class JavaBean
{
    private String foo;
    
    private String fruit;
    
    public JavaBean(String foo, String fruit)
    {
        this.foo = foo;
        this.fruit = fruit;
    }
}
